<!--
SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
SPDX-License-Identifier: LicenseRef-Apache-2.0
SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
SPDX-License-Identifier: GPL-3.0-or-later
-->

<p>You can still define resolveVar() and resolveFunc() methods in your custom expression classes, but you now also have the option to use variable blocks instead. These are useful for re-using variables and managing thread evaluation.

Within your custom expression class, you would skip the usual resolveVar() and resolveFunc() implementations:

<pre>
class MyExpr:public KSeExpr::Expression {

// These are now optional:
//   resolveVar()
//   resolveFunc()
};
</pre>


<p> Here's how you might write an example function to run an expression that uses variable blocks. This example assumes you have a particle data struct p, with numParticles, numAttributes. A variable block contains variable names and types, but doesn't care what the values are.

<pre>
void f(const std::string& s, MyParticleData* p, int outputDim=3){

    // set up the bindings for each of the particle attributes
    KSeExpr::VarBlockCreator blockCreator;
    std::vector<int> variableHandles;
    std::vector<MyParticleAttr> attrs;
    for(int i=0; i<p->numAttrs(); i++) {
        MyParticleAttr attr;
        p->getAttrInfo(i,attr);
        attrs.push_back(attr);
        int handle = blockCreator.registerVariable(attr.name,
                                                   TypeVec(attr.dim));
        variableHandles.push_back(handle);
    }

    // set up binding for the output
    int outputVariableHandle=blockCreator.registerVariable("__output",
                                                           TypeVec(outputDim));

    // make an expression with the appropriate bindings
    MyExpr e(s);
    e.setDesiredType(TypeVec(outputDim));
    e.setVarBlockCreator(&blockCreator);
    if(!e.isValid()) throw std::runtime_error(e.parseError()):

    // create the variable block, set up pointers to data
    KSeExpr::VarBlock block = blockCreator.create();
    for(int i=0; i<p->numAttrs(); i++) {
        block.Pointer(variableHandles[i]) = p->data<double>(attrs[i]);
    }
    std::vector<double> outputData(p->numParticles()*outputDim);
    block.Pointer(outputVariableHandle)=outputData.data();

    // evaluate multiple expressions at once; inlines expressions
    e.evalMultiple(&block, outputVariableHandle, 0, p->numParticles());
}
</pre>

To parallelize evaluation per particle, a simple parallel_for can be used:
<pre>
    // evaluate multiple expressions at once, inlines expressions
    tbb::parallel_for(blocked_range(0,p->numParticles()),[](blocked_range r)) {
        VarBlock myBlock=block.clone(); //should be cheap
        e.evalMultiple(&myblock, outputVariableHandle, r.start, r.end);
    }
</pre>
